import MathTool from "../lib/MathTool";
import ItemBaseData from "./data/ItemBaseData";
import PlayerData from "./data/PlayerData";
import Item from "./view/Item";

export default class Grid {
    private mRow: number = 0;
    private mCol: number = 11;
    private mGrid: Array<Array<Item>>;
    private mItemparent: Laya.Sprite;
    private mAreaCahae = {};
    private mTotalDropScore:number=0;
    constructor() {

    }

    public setItemParent(_sp: Laya.Sprite) {
        this.mItemparent = _sp
    }

    public setData(_r: number, _itemvos: Array<{ id: number, row: number, col: number }>) {
        this.setR(_r, true);
        let tdata;
        this.mTotalDropScore=0;
        let tvo:ItemBaseData;
        for (let i = 0; i < _itemvos.length; i++) {
            tdata = _itemvos[i];
            tvo =  this.addItemByData(tdata);
            this.mTotalDropScore += tvo.getDropScore();
        }
    }

    public setR(_row: number, _initGrid: boolean = false) {
        if (_initGrid) {
            this.mAreaCahae = {};
            this.clear();
            this.mRow = _row;
            let tcol;
            for (let row = 0; row < this.mRow; row++) {
                this.mGrid[row] = [];
                tcol = row % 2 == 0 ? this.mCol : this.mCol - 1
                for (let col = 0; col < tcol; col++) {
                    this.mGrid[row][col] = null;
                }
            }
        } else {
            this.mRow = _row;
        }
    }

    public addItem(_item: Item, _checkCanAdd: boolean = true) {
        if (_checkCanAdd && !this.canAdd(_item.data.row, _item.data.col, _item.data)) return false;
        if (this.isInLimit(_item.data.row, _item.data.col)) {
            if (!this.getItem(_item.data.row, _item.data.col)) {
                this.mGrid[_item.data.row][_item.data.col] = _item;
                this.mItemparent.addChild(_item);
                _item.updatePos();
                let tarea = _item.data.getAreaRCs();
                if (tarea) {
                    let trow: number;
                    let tcol: number;
                    for (let i = 0; i < tarea.length; i++) {
                        trow = tarea[i].row;
                        tcol = tarea[i].col;
                        if (this.getItem(trow, tcol) || !this.isInLimit(trow, tcol)) {
                            throw new Error("can,t insert");
                        };
                        this.mGrid[trow][tcol] = _item;
                        this.mAreaCahae[trow + "_" + tcol] = 1;
                    }
                }
                return true;
            }
        }
        return false;
    }

    public addItemByData(_data) {
        let tdata = new ItemBaseData();

        tdata.copy(_data, _data.id);
        if (tdata.isPao) {
            let tpaoid = PlayerData.getIns().currItemType * 5 + tdata.colorIndex;
            tdata.setDataById(tpaoid);
        }
        let titem = this.getItemInPool();
        titem.setData(tdata);
        this.addItem(titem, false);
        return tdata;
    }

    public addItemByPos(titem: Item, _x: number, _y: number, _checkCanAdd: boolean = true) {
        let trc = ItemBaseData.getRCByPos(_x, _y);
        titem.setRC(trc.row, trc.col);
        return this.addItem(titem, _checkCanAdd);
    }


    public canAddByPos(_idata: ItemBaseData, _x: number, _y: number) {
        let trc = ItemBaseData.getRCByPos(_x, _y);

        return this.canAdd(trc.row, trc.col, _idata);
    }

    public canAdd(_row: number, _col: number, _idata: ItemBaseData = null) {
        let trow: number = _row;
        let tcol = _col;
        if (this.isNotEmpty(trow, tcol) || !this.isInLimit(trow, tcol)) return false;
        let tarea = _idata ? _idata.getAreaRCs() : null;
        if (tarea) {
            for (let i = 0; i < tarea.length; i++) {
                if (this.isNotEmpty(tarea[i].row, tarea[i].col) || !this.isInLimit(tarea[i].row, tarea[i].col)) return false;
            }
        }
        if (trow == 0) {
            return true;
        } else {
            let tposs = ItemBaseData.getBoudsRcs(trow, tcol);
            for (let i = 0; i < tposs.length; i++) {
                if (this.getItem(tposs[i].row, tposs[i].col)) return true;
            }
        }
        return false;
    }

    public isNotEmpty(_row: number, _col: number) {
        if (this.getItem(_row, _col)) return true;
        return false;
    }

    public canMoveDown(_row: number, _col: number) {
        if (this.getItem(_row + 1, _col)) return false;
        this.mGrid[_row][_col] = null;
        return true;
    }

    public clear() {
        if (!this.mGrid) {
            this.mGrid = [];
            return;
        }
        for (let row = 0; row < this.mRow; row++) {
            if (!this.mGrid[row]) break;
            for (let col = 0; col < this.mCol; col++) {
                this.removeByRC(row, col);
            }
        }
        this.mGrid = [];
    }

    public removeByRC(_row: number, _col: number, _recover: boolean = true) {
        if (this.isInLimit(_row, _col)) {
            let titem = this.removeItem(this.mGrid[_row][_col], _recover);
            return titem;
        }
        return null;
    }


    public removeItem(_item: Item, _recover: boolean = true) {
        if (_item) {
            if (_recover) {
                _item.removeSelf();
                Laya.Pool.recover(ItemBaseData.ITEM_FLAG, _item);
            }
            this.clearGridData(_item);
            return _item;
        }
        return null;
    }

    public clearGridData(_item: Item) {
        this.mGrid[_item.data.row][_item.data.col] = null;
        let tarea = _item.data.getAreaRCs();
        if (tarea) {
            let trow: number;
            let tcol: number;
            for (let i = 0; i < tarea.length; i++) {
                trow = tarea[i].row;
                tcol = tarea[i].col;
                this.mGrid[trow][tcol] = null;
                this.mAreaCahae[trow + "_" + tcol] = null;
            }
        }
    }

    public removeItems(_arr: Array<Item>, _recover: boolean = true) {
        for (let i = 0; i < _arr.length; i++) {
            this.removeItem(_arr[i], _recover);
        }
    }

    public removeItemByPos(_x: number, _y: number, _recover: boolean = true) {
        let trc = ItemBaseData.getRCByPos(_x, _y);
        if (this.isInLimit(trc.row, trc.col)) {
            return this.removeItem(this.mGrid[trc.row][trc.col], _recover);
        }
        return null;
    }

    public getItem(_row: number, _col: number) {
        if (!this.isInLimit(_row, _col)) return null;
        if (!this.mGrid[_row]) return null;
        return this.mGrid[_row][_col];
    }

    public getItemByPos(_x: number, _y: number) {
        let trc = ItemBaseData.getRCByPos(_x, _y);
        return this.getItem(trc.row, trc.col);
    }

    public moveItem(_item: Item, _row: number, _col: number) {
        if (!this.canMoveDown(_item.data.row, _item.data.col)) {
            throw Error(_item.data.rcFlag + "有东西，无法移动");

        }
        let tarea = _item.data.getAreaRCs();
        if (tarea) {
            for (let i = 0; i < tarea.length; i++) {
                if (!this.canMoveDown(tarea[i].row, tarea[i].col)) {
                    throw Error(tarea[i].row + "_" + tarea[i].col + "有东西，无法移动");
                }
            }
        }
        _item.setRC(_row, _col);
        this.mGrid[_item.data.row][_item.data.col] = _item;
        if (tarea) {
            for (let i = 0; i < tarea.length; i++) {
                this.mGrid[tarea[i].row][tarea[i].col] = _item;
            }
        }
    }

    public isInLimit(_row: number, _col: number) {
        if (_row < 0 || _row >= this.mRow) return false;
        if (_col < 0 || _col >= (_row % 2 == 0 ? this.mCol : this.mCol - 1)) return false;
        return true;
    }

    public findSame(_item: Item, _cache = null) {

        let tposs1 = ItemBaseData.getBoudsRcs(_item.data.row, _item.data.col);
        let tsameItems: Array<Item> = [];
        let tflagItems: Array<Item> = [];
        if (!_cache || !_cache[_item.data.rcFlag]) tflagItems.push(_item);
        let tempItem: Item;
        let tcache = _cache ? _cache : {};
        tcache[_item.data.rcFlag] = 1;
        for (let i = 0; i < tposs1.length; i++) {
            tempItem = this.getItem(tposs1[i].row, tposs1[i].col);
            if (tempItem && tempItem.data.colorIndex == _item.data.colorIndex) {
                tsameItems.push(tempItem);
                tflagItems.push(tempItem);
            }
            tcache[tposs1[i].row + "_" + tposs1[i].col] = 1;
        }
        let titem: Item;
        while (tsameItems.length) {
            titem = tsameItems.pop();
            let tposs2 = ItemBaseData.getBoudsRcs(titem.data.row, titem.data.col);
            for (let i = 0; i < tposs2.length; i++) {
                if (tcache[tposs2[i].row + "_" + tposs2[i].col]) continue;
                tempItem = this.getItem(tposs2[i].row, tposs2[i].col);
                if (tempItem && tempItem.data.colorIndex == _item.data.colorIndex && !tcache[tempItem.data.rcFlag]) {
                    tsameItems.push(tempItem);
                    tflagItems.push(tempItem);
                }
                tcache[tposs2[i].row + "_" + tposs2[i].col] = 1;
            }
        }
        return tflagItems;
    }

    public findConnect(_item: Item, _cache, _minRow: number) {
        let tarr: Array<any> = [];
        if (!_cache[_item.data.rcFlag]) tarr.push(_item);
        _cache[_item.data.rcFlag] = 1;
        let tposs1 = ItemBaseData.getBoudsRcs(_item.data.row, _item.data.col);
        let tsameItems: Array<any> = [];
        let tempItem: Item;
        for (let i = 0; i < tposs1.length; i++) {
            if (_cache[tposs1[i].row + "_" + tposs1[i].col]) continue;
            tempItem = this.getItem(tposs1[i].row, tposs1[i].col);
            if (tempItem) {
                if (tposs1[i].row < _minRow) return tarr;
                if (this.mAreaCahae[tposs1[i].row + "_" + tposs1[i].col]) {
                    tsameItems.push({ row: tposs1[i].row, col: tposs1[i].col });
                    tarr.push({ row: tposs1[i].row, col: tposs1[i].col });
                } else {
                    tsameItems.push(tempItem);
                    tarr.push(tempItem);
                }

            }
            _cache[tposs1[i].row + "_" + tposs1[i].col] = 1;
        }
        let titem: any;
        let trow: number;
        let tcol: number;
        while (tsameItems.length) {
            titem = tsameItems.shift();
            if (titem instanceof Item) {
                trow = titem.data.row;
                tcol = titem.data.col;
            } else {
                trow = titem.row;
                tcol = titem.col;
            }
            let tposs2 = ItemBaseData.getBoudsRcs(trow, tcol);
            for (let i = 0; i < tposs2.length; i++) {
                if (_cache[tposs2[i].row + "_" + tposs2[i].col]) continue;
                tempItem = this.getItem(tposs2[i].row, tposs2[i].col);
                if (tempItem) {
                    if (tempItem.data.row < _minRow) return tarr;
                    if (this.mAreaCahae[tposs2[i].row + "_" + tposs2[i].col]) {
                        tsameItems.push({ row: tposs2[i].row, col: tposs2[i].col });
                        tarr.push({ row: tposs2[i].row, col: tposs2[i].col });
                    } else {
                        tsameItems.push(tempItem);
                        tarr.push(tempItem);
                    }

                }
                _cache[tposs2[i].row + "_" + tposs2[i].col] = 1;
            }
        }
        return tarr;
    }

    public findDropItems(_row: number = -1) {
        if (_row == -1) _row = this.getMaxItemRow();
        let tarr: Array<Array<any>> = [];
        if (_row > 0) {
            let tdist = _row - 20;
            let tmin = tdist >= 0 ? tdist : 0;
            let tcahe = {};
            let ttiem: Item;
            for (let row = _row; row >= tmin; row--) {
                for (let col = 0; col < this.mCol; col++) {
                    if (this.mAreaCahae[row + "_" + col]) continue;
                    ttiem = this.getItem(row, col);
                    if (ttiem) {
                        let tarr2 = this.findConnect(ttiem, tcahe, tmin);
                        if (tarr2.length) tarr.push(tarr2);
                    }
                }
            }
        }
        let ttaraarr = this.checkTwoArrDrop(tarr);
        return ttaraarr;
    }

    public checkTwoArrDrop(_arr: Array<Array<any>>) {
        let tarr: Array<Array<any>> = [];
        let tarr2;
        for (let i = 0; i < _arr.length; i++) {
            tarr2 = _arr[i];
            if (this.checkOneArrDrop(tarr2)) {
                this.filterArr(tarr2);
                tarr.push(tarr2);
            }
        }
        return tarr;
    }

    private filterArr(_arr: Array<any>) {
        for (let i = _arr.length - 1; i >= 0; i--) {
            if (!(_arr[i] instanceof Item)) _arr.splice(i, 1);
        }
    }
    public checkOneArrDrop(_arr: Array<any>) {
        if (!_arr.length) return false;
        let temp = {};
        let tindex = 0;
        let tdist = this.getMaxItemRow() - 20;
        let tmin = tdist >= 0 ? tdist : 0;
        let titem: any = _arr[tindex];
        let trow: number;
        let tcol: number;
        while (titem) {
            if (titem instanceof Item) {
                trow = titem.data.row;
                tcol = titem.data.col;
            } else {
                trow = titem.row;
                tcol = titem.col;
            }
            if (trow == tmin) return false;
            temp[trow + "_" + tcol] = 1;
            let trcs = ItemBaseData.getBoudsRcs(trow, tcol);
            trcs.sort(this.sortFunc);
            titem = null;
            for (let i = 0; i < trcs.length; i++) {
                if (temp[trcs[i].row + "_" + trcs[i].col]) continue;
                titem = this.getItem(trcs[i].row, trcs[i].col);
                if (titem) {
                    if (trcs[i].row == tmin) return false;
                    temp[trcs[i].row + "_" + trcs[i].col] = 1;
                    break;
                }
            }
            if (!titem) {
                tindex++;
                titem = _arr[tindex];
                if (!titem) return true;
            }
        }
        return false;
    }

    public checkCanAdd(_row: number, _col: number) {
        if (_row == 0) return true;
        let tminRow = this.getMaxItemRow() - 20;
        if (tminRow < 0) tminRow = 0;
        let trcs = ItemBaseData.getBoudsRcs(_row, _col);
        trcs.sort(this.sortFunc);
        let titem: Item = null;
        let temp = {};
        for (let i = 0; i < trcs.length; i++) {
            if (temp[trcs[i].row + "_" + trcs[i].col]) continue;
            titem = this.getItem(trcs[i].row, trcs[i].col);
            if (titem) {
                if (titem.data.row <= tminRow) return true;
                temp[trcs[i].row + "_" + trcs[i].col] = 1;
                break;
            }
        }
        while (titem) {
            if (titem.data.row <= tminRow) return true;
            let trcs = ItemBaseData.getBoudsRcs(titem.data.row, titem.data.col);
            trcs.sort(this.sortFunc);
            titem = null;
            for (let i = 0; i < trcs.length; i++) {
                if (temp[trcs[i].row + "_" + trcs[i].col]) continue;
                titem = this.getItem(trcs[i].row, trcs[i].col);
                if (titem) {
                    if (titem.data.row <= tminRow) return true;
                    temp[trcs[i].row + "_" + trcs[i].col] = 1;
                    break;
                }
            }
            if (!titem) return false;
        }
        return false;
    }

    private sortFunc(a, b) {
        if (a.row < b.row) return -1;
        else if (a.row < b.row) return 1;
        return 0;
    }

    public getItemsByDist(_row: number, _col: number, _dist: number = 60) {
        let tarr = [];
        let tpos = ItemBaseData.getPosByRC(_row, _col);
        let tminRow = ItemBaseData.getRCByPos(tpos.x, tpos.y - _dist).row;
        if (tminRow < 0) tminRow = 0;
        let tmaxRow = ItemBaseData.getRCByPos(tpos.x, tpos.y + _dist).row;
        if (tmaxRow > this.mRow - 1) tmaxRow = this.mRow - 1;
        let titem: Item;
        for (let row = tminRow; row <= tmaxRow; row++) {
            for (let col = 0; col < this.col; col++) {
                titem = this.getItem(row, col);
                if (titem && MathTool.getTwoPointDist(tpos.x, tpos.y, titem.x, titem.y) <= _dist) {
                    tarr.push(titem);
                }
            }
        }
        return tarr;
    }
    public getMaxItemRow() {
        let trow = 0;
        for (let row = this.mRow - 1; row >= 0; row--) {
            trow = row;
            for (let col = 0; col < this.mCol; col++) {
                if (this.getItem(row, col)) {
                    return trow;
                }
            }
        }
        return trow;
    }

    public getPaoTypeNum() {
        let tnum = 0;
        let tcche = {};
        let titem: Item;
        for (let row = 0; row <= this.mRow; row++) {
            for (let col = 0; col < this.mCol; col++) {
                titem = this.getItem(row, col);
                if (titem && titem.data.isPao) {
                    if (!tcche[titem.data.id]) {
                        tcche[titem.data.id] = 1;
                        tnum++;
                    }
                }
            }
        }
        return tnum;
    }

    public getPaoIds() {
        let tarr = [];
        let titem: Item;
        let trow = this.getMaxItemRow();
        let tminrow = trow - 20;
        if (tminrow < 0) tminrow = 0;
        for (let row = trow; row >= tminrow; row--) {
            if (tarr.length >= 5) break;
            for (let col = 0; col < this.mCol; col++) {
                titem = this.getItem(row, col);
                if (titem && titem.data.isPao) {
                    if (tarr.indexOf(titem.data.id) < 0) {
                        tarr.push(titem.data.id);
                    }
                }
            }
        }
        return tarr;
    }

    public getGridItemStr() {
        let tstr = "";
        let titem: Item;
        for (let row = 0; row < this.mRow; row++) {
            for (let col = 0; col < this.mCol; col++) {
                titem = this.mGrid[row][col];
                if (titem) {
                    if (!tstr) tstr = titem.getStr();
                    else tstr += "|" + titem.getStr();
                }
            }
        }
        return tstr
    }

    public getItemInPool() {
        let titm: Item = Laya.Pool.getItem(ItemBaseData.ITEM_FLAG);
        if (!titm) titm = new Item();
        return titm;
    }
    /** num1=pao num2=diamand num3=colorDiamand*/
    public getTaskNum() {
        let titm: Item;
        let tnum: number = 0;
        let tnum2: number = 0;
        let tnum3: number = 0;
        let tcache = {};
        for (let row = 0; row < this.mRow; row++) {
            for (let col = 0; col < this.mCol; col++) {
                titm = this.getItem(row, col);
                if (titm) {
                    if (titm.data.isPao) tnum++;
                    else if (titm.data.isDiamand) {
                        if (!tcache[titm.data.rcFlag]) {
                            tnum2++;
                            tcache[titm.data.rcFlag] = 1;
                        }
                    }
                    else if (titm.data.isColorDiamand) {
                        if (!tcache[titm.data.rcFlag]) {
                            tnum3++;
                            tcache[titm.data.rcFlag] = 1;
                        }
                    }
                }
            }
        }
        return { num1: tnum, num2: tnum2, num3: tnum3 };
    }

    public isUpOut(_x: number, _y: number) {
        let tp = this.itemParent.localToGlobal(new Laya.Point(_x, _y));
        if (tp.y < -10) return true;
        return false;
    }

    public get itemParent() {
        return this.mItemparent;
    }

    public get row(): number {
        return this.mRow;
    }

    public get col(): number {
        return this.mCol;
    }

    public get totalDropScore(){
        return this.mTotalDropScore;
    }
}